export interface TweenOptions {
  targets: Record<string, any>[];
  attrs: Record<string, number>;
  duration: number;
  easing?: ((progress: number)=>number);
  yoyo?: boolean;
  repeat?: number;
};

enum TWEEN_STATE {
  RUNNING = 1,
  PAUSED,
  STOP,
};

export class Tween {
  private state: TWEEN_STATE = TWEEN_STATE.PAUSED;

  private targets: Record<string, any>[];

  private targetStatesMap: Map<Record<string, any>, Record<string, {from: number, to: number}>> = new Map();

  private duration: number;

  private easing: (progress: number)=>number;

  private yoyo = false;

  private repeat = 1;

  private startTime: number;

  private pausedTime: number;

  private pausedDuration: number = 0;

  private isYoyoRuning = false;

  constructor (options: TweenOptions) {
    const { targets, attrs, duration, easing, yoyo = false, repeat = 1, } = options;

    this.targets = targets;
    this.duration = duration;
    this.easing = easing;
    this.yoyo = yoyo;
    this.repeat = repeat;

    this.targets.forEach(target => {
      const keys = Object.keys(attrs);
      const attrStates = {}
      for (const key of keys) {
        if (key in target) {
          attrStates[key] = {
            from: target[key],
            to: attrs[key],
          }
        }
      }
      this.targetStatesMap.set(target, attrStates);
    });
  }

  private yoyoStates () {
    this.targetStatesMap.forEach((attrStates, target) => {
      for (const [key, value] of Object.entries(attrStates)) {
        const from = value.from;
        value.from = value.to;
        value.to = from;
      }
    });
  }

  update (elapsedTime?: number) {
    if (this.state !== TWEEN_STATE.RUNNING) {
      return;
    }

    const now = Date.now();
    const totalElapsedTime = now - this.startTime - this.pausedDuration;
    let progress = totalElapsedTime / this.duration;
    const originalProgress = progress;
    if (this.easing) {
      progress = this.easing(progress);
    }
    if (originalProgress >= 1) {
      progress = 1;
    }

    this.targetStatesMap.forEach((attrStates, target) => {
      for (const [key, {from, to}] of Object.entries(attrStates)) {
        const v = from + (to - from) * progress;
        target[key] = v;
      }
    });

    if (progress >= 1) {
      // console.log('yoyo', this.yoyo, this.isYoyoRuning, this.repeat);
      if (this.yoyo) {
        if (!this.isYoyoRuning) {
          this.isYoyoRuning = true;
          this.yoyoStates();
          this.startTime = Date.now();
          this.pausedTime = 0;
          this.pausedDuration = 0;
        } else {
          this.isYoyoRuning = false;
          this.yoyoStates();
          if (this.repeat >= 0) {
            --this.repeat;
          }
        }
      } else {
        --this.repeat;
      }

      if (this.repeat !== 0) {
        this.targetStatesMap.forEach((attrStates, target) => {
          for (const [key, {from, to}] of Object.entries(attrStates)) {
            target[key] = from;
          }
        });
        this.startTime = Date.now();
        this.pausedTime = 0;
        this.pausedDuration = 0;
      } else {
        this.state = TWEEN_STATE.STOP;
      }
    }
  }

  start () {
    if (!this.state || this.state === TWEEN_STATE.PAUSED) {
      this.state = TWEEN_STATE.RUNNING;
    }
    if (!this.startTime) {
      this.startTime = Date.now();
    }
    if (this.pausedTime) {
      this.pausedDuration += Date.now() - this.pausedTime;
      this.pausedTime = 0;
    }
  }

  pause () {
    if (this.state === TWEEN_STATE.RUNNING) {
      this.state = TWEEN_STATE.PAUSED;
      this.pausedTime = Date.now();
    }
  }
}
